<?php
namespace Pinenut;

/**
 * Контекст выполнения запроса
 * 
 * Реализует доступ к данным выполнения запроса 
 * и некоторым функциям работы с этими данными
 * Инициируется приложением и используется диспечером
 * 
 * @author Kosh
 *
 */
class Context {
	
	/**
	 * Стек запросов - ключи
	 * @var string[]
	 */
	public $requestsStack = array();
	
	/**
	 * Дочерние запросы - каждый элемент имеет ключ родительского запроса и массив дочерних ключей
	 * @var array
	 */
	public $childrenRequests = array();
	
	/**
	 * Основной запрос
	 * @var \Pinenut\Request
	 */
	public $mainRequest;
	
	
	/**
	 * Глобальные параметры
	 * @var array
	 */
	public $globalParams = [];
	
	/**
	 * Все запросы (основной, вторичные)
	 * @var unknown_type
	 */
	public $requests = array();
	
	/**
	 * Все ответы
	 * @var unknown_type
	 */
	public $responses = array();
	
	/**
	 * Текущий пользователь
	 * @var \Pinenut\Context\Auth
	 */
	public $auth = null;
	
	/**
	 * Инициируем контекст запросом
	 * @param \Pinenut\Request $mainRequest
	 */
	function __construct(\Pinenut\Request $mainRequest) {
		$this->mainRequest = $mainRequest;
		// получаем и сбрасываем глобальные парамаетры
		// чтобы их единообразно устанавливать из диспечера
		// а за одно фильтруем
		$this->globalParams = static::ignoreParams($mainRequest->globalParams(), 
			$mainRequest->getActionClassName());
		
		// сбрасываем глобальные параметры
		// TODO: рожать контекст до маршрутизации, чтобы глобальные параметры 
		// устанавливать в контекст а не в реквест
		$mainRequest->globalParams([]);
		
		$this->auth = new \Auth;

		$this->onStartContext();
	}
	
	/**
	 * Нулячий контекст, еще ничего не делали
	 */
	function onStartContext() {

	}
	
	
	/**
	 * Перед выполнением устанавливаем глобалы
	 * @param \Pinenut\Request $request
	 */
	function setGlobalParams(\Pinenut\Request $request) {
		$params = $request->params();
		$params = array_merge($params, $this->globalParams);
		$request->params($params);
	}
	

	/**
	 * Техническая фильтрация параметров на основании списка игнорирования действия
	 * @param array $params
	 * @param string $actionClassName
	 * @return array
	 */
	static function ignoreParams($params, $actionClassName) {
		// игнорим все
		$ignoreParams = $actionClassName::ignoreParams ();
		if (! empty ( $ignoreParams)) {
			// вычисляем не указанные элементы и грохаем их
			foreach ( $ignoreParams as $key ) {
				unset ( $params [$key] );
				// вместо сбрасывания используем null
				//$params [$key] = null;
			}
		}
		
		return $params;
	}
	
	/**
	 * Спускаемся внутрь дерева запросов, добавляя новый запрос на выполнение в стек
	 * @param \Pinenut\Request $request
	 * @param boolean $toRoot добавляем не к текущему, а в корневой
	 * @return string
	 */
	function pushRequest(\Pinenut\Request $request) {
		$key = $request->getKey();
		$this->requests[$key] = $request;
		
		// для ведения дочерних элементов берём родителя и добавляем в его список вновьприбывшего
		@$this->childrenRequests[end($this->requestsStack)][] = $key;
			
		$this->requestsStack[] = $key;

		return $key;
	}
	
	function deferredRequest(\Pinenut\Request $request) {
		$key = $request->getKey();
		$this->requests[$key] = $request;

		@$this->childrenRequests['deferred'][] = $key;
		
		return $key;
	}
	
	
	function getDeferredRequests() {
		$ids = (array)$this->childrenRequests['deferred'];
		$requests = [];
		foreach ($ids as $id) {
			$requests[$id] = $this->requests[$id];
		}
		return $requests;
	}
	
	function cleanDeferredRequests() {
		$this->childrenRequests['deferred'] = [];
	}
	
	/**
	 * Выходим уровнем выше
	 * @return string
	 */
	function popRequest() {
		return array_pop($this->requestsStack);
	}
	
	function getCurrentRequest() {
		return $this->requests[end($this->requestsStack)];
	}
	
	function getMainRequest() {
		return $this->mainRequest;
	}
	
	function addResponse(\Pinenut\Response $response) {
		$this->responses[end($this->requestsStack)] = $response;
	}
	
	function getResponse() {
		return @$this->responses[end($this->requestsStack)];
	}
	
	function getChildrenResponses($requestKey = null) {
		$requestKey = null === $requestKey
					? end($this->requestsStack)
					: $requestKey;
		$childrenRequests = @$this->childrenRequests[$requestKey];
		$responses = array();
		if (is_array($childrenRequests)) {
			foreach ($childrenRequests as $key) {
				if (isset($this->responses[$key])) {
					$responses[] = $this->responses[$key];
				}
			}
		}
		return $responses;
	}
	
	/**
	 * Проверка прав доступа
	 * 
	 * @param string|array $privileges
	 * @return bool
	 */
	function checkAccess($privileges, $returnChecks = false) {
		if (empty($privileges)) 
			return $returnChecks?[]:true;
		else 
			return $this->auth->checkAccess($privileges, $returnChecks);
	}
	
}